#ifndef FILEEXPLORERUPDATER_H_INCLUDED
#define FILEEXPLORERUPDATER_H_INCLUDED

#include <wx/wx.h>
#include <wx/treectrl.h>
#include <wx/thread.h>
#include <wx/process.h>

#include <queue>
#include <vector>
class VCSstatearray;
class FileExplorer;
class wxStringOutputStream;

// SIMPLE wxCommandEvent DERIVED CUSTOM EVENTS THAT USE THE BUILTIN EVT_COMMAND EVENT TABLE ENTRY
BEGIN_DECLARE_EVENT_TYPES()
DECLARE_LOCAL_EVENT_TYPE( wxEVT_NOTIFY_EXEC_REQUEST, -1 )
DECLARE_LOCAL_EVENT_TYPE( wxEVT_NOTIFY_UPDATE_COMPLETE, -1 )
DECLARE_LOCAL_EVENT_TYPE( wxEVT_NOTIFY_LOADER_UPDATE_COMPLETE, -1 )
DECLARE_LOCAL_EVENT_TYPE( wxEVT_NOTIFY_COMMITS_UPDATE_COMPLETE, -1 )
END_DECLARE_EVENT_TYPES()


struct CommitEntry {
  wxString id;
  wxString author;
  wxString message;
  wxString date;
};

struct FileData {
  wxString name;
  int state;
  // could also add full path, modified time
};

typedef std::vector<FileData> FileDataVec;

class Updater: public wxEvtHandler, public wxThread {
  public:
    Updater() : wxThread( wxTHREAD_JOINABLE ) {
      m_kill = false;
      m_exec_mutex = NULL;
      m_exec_proc = NULL;
      m_exec_stream = NULL;
      m_exec_sstream = NULL;
      m_exec_timer = NULL;
      m_exec_cond = NULL;
      m_cancelled = false;
    }
    ~Updater();
    bool m_cancelled;
  protected:
    // Thread and process handling -- generic stuff
    virtual ExitCode Entry() = 0;
    int Exec( const wxString &command, wxArrayString &output, const wxString &path = wxEmptyString );
    int Exec( const wxString &command, wxString &output, const wxString &path = wxEmptyString );
    wxMutex *m_exec_mutex;
    wxCondition *m_exec_cond;
    wxProcess *m_exec_proc;
    wxInputStream *m_exec_stream;
    wxStringOutputStream *m_exec_sstream;
    int m_exec_proc_id;
    wxTimer *m_exec_timer;
    wxString m_exec_cmd;
    wxString m_exec_path;
    bool m_kill;
    wxString m_exec_output;
  private:
    void ReadStream( bool all = false );
    void OnExecMain( wxCommandEvent &event );
    void OnExecTerminate( wxProcessEvent &e );
    void OnExecTimer( wxTimerEvent &e );
    DECLARE_EVENT_TABLE()
};

class FileExplorerUpdater: public Updater {
  public:
    FileExplorerUpdater( FileExplorer *fe ) : Updater() {
      m_fe = fe;
    }
    ~FileExplorerUpdater() {}
    FileDataVec m_adders;
    FileDataVec m_removers;
    wxString m_vcs_type;
    wxString m_vcs_commit_string;
    bool m_vcs_changes_only;
    void Update( const wxTreeItemId &ti ); //call on main thread to do the background magic
  private:
    //FileExplorerUpdater specific code
    virtual ExitCode Entry(); //Entry point of the thread
    FileExplorer *m_fe;
    FileDataVec m_treestate;
    FileDataVec m_currentstate;
    wxString m_path;
    wxString m_repo_path;
    wxString m_wildcard;
    bool ParseGITChangesTree( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseSVNChangesTree( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseBZRChangesTree( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseHGChangesTree( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseGITChanges( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseBZRChanges( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseHGChanges( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseSVNChanges( const wxString &path, VCSstatearray &sa, bool relative_paths = false );
    bool ParseCVSChanges( const wxString &path, VCSstatearray &sa );
    bool GetGITCommitState( const wxString &path );
    bool GetHgCommitState( const wxString &path );
    bool GetVCSCommitState( const wxString &path, const wxString &cmd );
    void GetTreeState( const wxTreeItemId &ti );
    bool GetCurrentState( const wxString &path );
    bool CalcChanges(); //creates the vector of adders and removers
};

struct LoaderQueueItem {
  wxString op;
  wxString source;
  wxString destination;
  wxString comp_commit;
};

class LoaderQueue: public std::queue<LoaderQueueItem> {
  public:
    void Add( const LoaderQueueItem &item ) { push( item ); }
    void Add( const wxString &op, const wxString &source, const wxString &destination, const wxString &comp_commit = wxEmptyString ) { LoaderQueueItem item; item.op = op; item.source = source; item.destination = destination; item.comp_commit = comp_commit; push( item );}
    LoaderQueueItem Pop() { LoaderQueueItem it = front(); pop(); return it; }
};

class VCSFileLoader: public Updater {
  public:
    VCSFileLoader( FileExplorer *fe ) : Updater() {
      m_fe = fe;
    }
    void Update( const wxString &op, const wxString &source_path, const wxString &destination_path, //call on main thread to do the background magic
                 const wxString &comp_commit );
    wxString m_source_path;
    wxString m_destination_path;
  private:
    virtual ExitCode Entry(); //Entry point of the thread
    wxString m_vcs_type;
    wxString m_vcs_commit_string;
    wxString m_vcs_comp_commit;
    wxString m_vcs_op;
    FileExplorer *m_fe;
    wxString m_repo_path;
};

class CommitUpdaterOptions {
  public:
    CommitUpdaterOptions() {}
    CommitUpdaterOptions( const wxString &file0, const wxString &grep0, const wxString &start_commit0, const wxString &end_commit0,
                          const wxString &date_before0, const wxString &date_after0, long commits_per_retrieve0 ) {
      file = wxString( file0.c_str() );
      grep = wxString( grep0.c_str() );
      start_commit = wxString( start_commit0.c_str() );
      end_commit = wxString( end_commit0.c_str() );
      date_before = wxString( date_before0.c_str() );
      date_after = wxString( date_after0.c_str() );
      commits_per_retrieve = commits_per_retrieve0;
    }
    wxString file;
    wxString grep;
    wxString date_before;
    wxString date_after;
    wxString start_commit;
    wxString end_commit;
    unsigned long commits_per_retrieve;
};

class CommitUpdater: public Updater {
  public:
    CommitUpdater( wxEvtHandler *parent, const wxString& repo_path, const wxString &repo_type ) : Updater() {
      m_parent = parent;
      m_repo_path = wxString( repo_path.c_str() );
      m_repo_type = repo_type;
    }
    CommitUpdater( const CommitUpdater& updater );
    ~CommitUpdater() {}

    //call on main thread to do the background magic
    //what can be "BRANCHES", "COMMITS:<BRANCHNAME>", "DETAIL:<COMMITID>"
    bool Update( const wxString &what, const wxString &repo_branch = wxEmptyString,
                 CommitUpdaterOptions opts = CommitUpdaterOptions() );
    bool UpdateContinueCommitRetrieve();

    //Inputs
    CommitUpdaterOptions m_opts;
    wxString m_what;
    wxString m_repo_path;
    wxString m_repo_type;
    wxString m_repo_branch;
    wxEvtHandler *m_parent;

    //Outputs
    wxString m_detailed_commit_log; //the detailed commit log (if what == "DETAIL:")
    bool m_retrieved_all; // false if there are more commits to retrieve (if what == "COMMITS:")
    std::vector<CommitEntry> m_commits;
    wxArrayString m_branches;

  private:
    wxString m_last_commit_retrieved; // the ID of the last commit retrieved (only filled if what == "COMMIT:")
    long m_continue_count;

    virtual ExitCode Entry(); //Entry point of the thread
};

#endif //FILEEXPLORERUPDATER_H_INCLUDED
